#include <mcp/server/lifespan_manager.h>
#include <mcp/error.h>
#include <mcp/json/value.h>
#include <iostream>
#include <sstream>
#include <memory>
#include <stdexcept>

namespace mcp {
namespace server {

// Base LifespanManager implementation
struct LifespanManagerBase::Impl {
    std::string manager_id;
    std::chrono::steady_clock::time_point creation_time;
    mutable std::mutex state_mutex;
    LifespanState state;

    Impl(const std::string& id)
        : manager_id(id), state(LifespanState::NotInitialized),
          creation_time(std::chrono::steady_clock::now()) {}
};

LifespanManagerBase::LifespanManagerBase(const std::string& id)
    : impl_(std::make_unique<Impl>(id)) {}

LifespanManagerBase::~LifespanManagerBase() = default;

std::string LifespanManagerBase::manager_id() const {
    return impl_->manager_id;
}

LifespanState LifespanManagerBase::state() const {
    std::lock_guard<std::mutex> lock(impl_->state_mutex);
    return impl_->state;
}

std::chrono::steady_clock::duration LifespanManagerBase::uptime() const {
    return std::chrono::steady_clock::now() - impl_->creation_time;
}

void LifespanManagerBase::set_state(LifespanState new_state) {
    std::lock_guard<std::mutex> lock(impl_->state_mutex);
    impl_->state = new_state;
}

// Void lifespan manager implementation
struct VoidLifespanManager::Impl {
    VoidContext context;
    StartupFunction startup_func;
    ShutdownFunction shutdown_func;
    bool startup_called = false;
    bool shutdown_called = false;
    mutable std::mutex mutex;

    Impl(StartupFunction startup, ShutdownFunction shutdown)
        : startup_func(std::move(startup)), shutdown_func(std::move(shutdown)) {}
};

VoidLifespanManager::VoidLifespanManager(StartupFunction startup, ShutdownFunction shutdown)
    : LifespanManagerBase("void_lifespan"), impl_(std::make_unique<Impl>(std::move(startup), std::move(shutdown))) {}

VoidLifespanManager::~VoidLifespanManager() {
    // Ensure shutdown is called if startup was called
    if (impl_->startup_called && !impl_->shutdown_called) {
        try {
            shutdown();
        } catch (...) {
            // Ignore errors during destruction
        }
    }
}

Result<VoidContext> VoidLifespanManager::initialize() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (impl_->startup_called) {
        return Error::invalid_state("Lifespan manager already initialized");
    }

    try {
        if (impl_->startup_func) {
            impl_->startup_func();
        }
        impl_->startup_called = true;
        set_state(LifespanState::Initialized);
        return VoidContext{};
    } catch (const std::exception& e) {
        return Error::internal_error(std::string("Lifespan startup failed: ") + e.what());
    }
}

void VoidLifespanManager::shutdown() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (!impl_->startup_called || impl_->shutdown_called) {
        return;
    }

    try {
        if (impl_->shutdown_func) {
            impl_->shutdown_func();
        }
        impl_->shutdown_called = true;
        set_state(LifespanState::Shutdown);
    } catch (const std::exception& e) {
        set_state(LifespanState::Error);
        throw;
    }
}

VoidContext VoidLifespanManager::get_context() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (!impl_->startup_called) {
        throw std::runtime_error("Lifespan not initialized");
    }
    return impl_->context;
}

// Database connection manager example implementation
struct DatabaseConnectionManager::Impl {
    struct ConnectionInfo {
        std::string connection_string;
        bool connected = false;
        std::chrono::steady_clock::time_point connect_time;
        std::vector<std::string> tables;
    };

    ConnectionInfo connection;
    mutable std::mutex mutex;

    Impl(const std::string& conn_str) : connection{conn_str, false} {}
};

DatabaseConnectionManager::DatabaseConnectionManager(const std::string& connection_string)
    : LifespanManagerBase("database_lifespan"), impl_(std::make_unique<Impl>(connection_string)) {}

DatabaseConnectionManager::~DatabaseConnectionManager() = default;

Result<DatabaseContext> DatabaseConnectionManager::initialize() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (impl_->connection.connected) {
        return Error::invalid_state("Database already connected");
    }

    try {
        // Simulate database connection
        std::cout << "Connecting to database: " << impl_->connection.connection_string << std::endl;

        // In a real implementation, this would establish actual database connection
        impl_->connection.connected = true;
        impl_->connection.connect_time = std::chrono::steady_clock::now();

        // Simulate getting table list
        impl_->connection.tables = {"users", "products", "orders", "logs"};

        DatabaseContext context{
            .connection_string = impl_->connection.connection_string,
            .connected = true,
            .tables = impl_->connection.tables
        };

        set_state(LifespanState::Initialized);
        return context;
    } catch (const std::exception& e) {
        return Error::internal_error(std::string("Database connection failed: ") + e.what());
    }
}

void DatabaseConnectionManager::shutdown() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (!impl_->connection.connected) {
        return;
    }

    try {
        std::cout << "Disconnecting from database: " << impl_->connection.connection_string << std::endl;

        // In a real implementation, this would close the database connection
        impl_->connection.connected = false;

        set_state(LifespanState::Shutdown);
    } catch (const std::exception& e) {
        set_state(LifespanState::Error);
        throw;
    }
}

DatabaseContext DatabaseConnectionManager::get_context() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (!impl_->connection.connected) {
        throw std::runtime_error("Database not connected");
    }

    return DatabaseContext{
        .connection_string = impl_->connection.connection_string,
        .connected = true,
        .tables = impl_->connection.tables
    };
}

std::vector<std::string> DatabaseConnectionManager::query_tables() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (!impl_->connection.connected) {
        throw std::runtime_error("Database not connected");
    }
    return impl_->connection.tables;
}

std::chrono::steady_clock::duration DatabaseConnectionManager::connection_uptime() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (!impl_->connection.connected) {
        throw std::runtime_error("Database not connected");
    }
    return std::chrono::steady_clock::now() - impl_->connection.connect_time;
}

// HTTP service manager implementation
struct HttpServiceManager::Impl {
    struct ServiceInfo {
        std::string base_url;
        int port;
        bool running = false;
        std::chrono::steady_clock::time_point start_time;
        std::vector<std::string> endpoints;
    };

    ServiceInfo service;
    mutable std::mutex mutex;

    Impl(const std::string& url, int port) : service{url, port} {}
};

HttpServiceManager::HttpServiceManager(const std::string& base_url, int port)
    : LifespanManagerBase("http_service_lifespan"), impl_(std::make_unique<Impl>(base_url, port)) {}

HttpServiceManager::~HttpServiceManager() = default;

Result<HttpContext> HttpServiceManager::initialize() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (impl_->service.running) {
        return Error::invalid_state("HTTP service already running");
    }

    try {
        std::cout << "Starting HTTP service on " << impl_->service.base_url << ":" << impl_->service.port << std::endl;

        // In a real implementation, this would start an HTTP server
        impl_->service.running = true;
        impl_->service.start_time = std::chrono::steady_clock::now();

        // Simulate registering endpoints
        impl_->service.endpoints = {
            "/api/health",
            "/api/tools",
            "/api/resources",
            "/api/prompts"
        };

        HttpContext context{
            .base_url = impl_->service.base_url,
            .port = impl_->service.port,
            .running = true,
            .endpoints = impl_->service.endpoints
        };

        set_state(LifespanState::Initialized);
        return context;
    } catch (const std::exception& e) {
        return Error::internal_error(std::string("HTTP service startup failed: ") + e.what());
    }
}

void HttpServiceManager::shutdown() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (!impl_->service.running) {
        return;
    }

    try {
        std::cout << "Stopping HTTP service on port " << impl_->service.port << std::endl;

        // In a real implementation, this would stop the HTTP server
        impl_->service.running = false;

        set_state(LifespanState::Shutdown);
    } catch (const std::exception& e) {
        set_state(LifespanState::Error);
        throw;
    }
}

HttpContext HttpServiceManager::get_context() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (!impl_->service.running) {
        throw std::runtime_error("HTTP service not running");
    }

    return HttpContext{
        .base_url = impl_->service.base_url,
        .port = impl_->service.port,
        .running = true,
        .endpoints = impl_->service.endpoints
    };
}

std::vector<std::string> HttpServiceManager::get_endpoints() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (!impl_->service.running) {
        throw std::runtime_error("HTTP service not running");
    }
    return impl_->service.endpoints;
}

std::chrono::steady_clock::duration HttpServiceManager::service_uptime() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (!impl_->service.running) {
        throw std::runtime_error("HTTP service not running");
    }
    return std::chrono::steady_clock::now() - impl_->service.start_time;
}

// Combined lifespan manager implementation
struct CombinedLifespanManager::Impl {
    std::vector<std::unique_ptr<LifespanManagerBase>> managers;
    std::vector<std::string> initialization_order;
    mutable std::mutex mutex;

    Impl() = default;
};

CombinedLifespanManager::CombinedLifespanManager()
    : LifespanManagerBase("combined_lifespan"), impl_(std::make_unique<Impl>()) {}

CombinedLifespanManager::~CombinedLifespanManager() = default;

void CombinedLifespanManager::add_manager(std::unique_ptr<LifespanManagerBase> manager) {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    impl_->managers.push_back(std::move(manager));
}

Result<CombinedContext> CombinedLifespanManager::initialize() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (!impl_->initialization_order.empty()) {
        return Error::invalid_state("Combined lifespan manager already initialized");
    }

    CombinedContext combined_context;
    std::vector<std::string> errors;

    // Initialize all managers in order
    for (auto& manager : impl_->managers) {
        try {
            auto context = manager->initialize();
            if (!context) {
                errors.push_back(manager->manager_id() + ": " + context.error().message());
                continue;
            }

            // Store context based on manager type
            if (auto void_mgr = dynamic_cast<VoidLifespanManager*>(manager.get())) {
                // Void context is stored as a flag
                combined_context.has_void_context = true;
            } else if (auto db_mgr = dynamic_cast<DatabaseConnectionManager*>(manager.get())) {
                combined_context.database_context = db_mgr->get_context();
            } else if (auto http_mgr = dynamic_cast<HttpServiceManager*>(manager.get())) {
                combined_context.http_context = http_mgr->get_context();
            }

            impl_->initialization_order.push_back(manager->manager_id());
        } catch (const std::exception& e) {
            errors.push_back(manager->manager_id() + ": " + e.what());
        }
    }

    if (!errors.empty()) {
        // Shutdown any successfully initialized managers
        for (auto it = impl_->initialization_order.rbegin(); it != impl_->initialization_order.rend(); ++it) {
            for (auto& manager : impl_->managers) {
                if (manager->manager_id() == *it) {
                    try {
                        manager->shutdown();
                    } catch (...) {
                        // Continue shutdown despite errors
                    }
                    break;
                }
            }
        }
        impl_->initialization_order.clear();

        std::string error_msg = "Combined initialization failed:\n";
        for (const auto& error : errors) {
            error_msg += "  - " + error + "\n";
        }
        return Error::internal_error(error_msg);
    }

    set_state(LifespanState::Initialized);
    return combined_context;
}

void CombinedLifespanManager::shutdown() {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    if (impl_->initialization_order.empty()) {
        return;
    }

    // Shutdown in reverse order
    std::vector<std::string> errors;
    for (auto it = impl_->initialization_order.rbegin(); it != impl_->initialization_order.rend(); ++it) {
        for (auto& manager : impl_->managers) {
            if (manager->manager_id() == *it) {
                try {
                    manager->shutdown();
                } catch (const std::exception& e) {
                    errors.push_back(manager->manager_id() + ": " + e.what());
                }
                break;
            }
        }
    }

    impl_->initialization_order.clear();

    if (!errors.empty()) {
        set_state(LifespanState::Error);
        std::string error_msg = "Combined shutdown had errors:\n";
        for (const auto& error : errors) {
            error_msg += "  - " + error + "\n";
        }
        throw std::runtime_error(error_msg);
    }

    set_state(LifespanState::Shutdown);
}

CombinedContext CombinedLifespanManager::get_context() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    if (impl_->initialization_order.empty()) {
        throw std::runtime_error("Combined lifespan not initialized");
    }

    CombinedContext context;
    for (const auto& manager : impl_->managers) {
        if (auto void_mgr = dynamic_cast<VoidLifespanManager*>(manager.get())) {
            context.has_void_context = true;
        } else if (auto db_mgr = dynamic_cast<DatabaseConnectionManager*>(manager.get())) {
            context.database_context = db_mgr->get_context();
        } else if (auto http_mgr = dynamic_cast<HttpServiceManager*>(manager.get())) {
            context.http_context = http_mgr->get_context();
        }
    }

    return context;
}

std::vector<std::string> CombinedLifespanManager::get_manager_ids() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    std::vector<std::string> ids;
    for (const auto& manager : impl_->managers) {
        ids.push_back(manager->manager_id());
    }
    return ids;
}

std::vector<LifespanState> CombinedLifespanManager::get_manager_states() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    std::vector<LifespanState> states;
    for (const auto& manager : impl_->managers) {
        states.push_back(manager->state());
    }
    return states;
}

// Utility functions
std::string lifespan_state_to_string(LifespanState state) {
    switch (state) {
        case LifespanState::NotInitialized:
            return "NotInitialized";
        case LifespanState::Initializing:
            return "Initializing";
        case LifespanState::Initialized:
            return "Initialized";
        case LifespanState::ShuttingDown:
            return "ShuttingDown";
        case LifespanState::Shutdown:
            return "Shutdown";
        case LifespanState::Error:
            return "Error";
        default:
            return "Unknown";
    }
}

// Factory functions
std::unique_ptr<VoidLifespanManager> create_void_lifespan(
    std::function<void()> startup,
    std::function<void()> shutdown) {
    return std::make_unique<VoidLifespanManager>(std::move(startup), std::move(shutdown));
}

std::unique_ptr<DatabaseConnectionManager> create_database_lifespan(const std::string& connection_string) {
    return std::make_unique<DatabaseConnectionManager>(connection_string);
}

std::unique_ptr<HttpServiceManager> create_http_service_lifespan(const std::string& base_url, int port) {
    return std::make_unique<HttpServiceManager>(base_url, port);
}

std::unique_ptr<CombinedLifespanManager> create_combined_lifespan() {
    return std::make_unique<CombinedLifespanManager>();
}

} // namespace server
} // namespace mcp